#include "WIceAgent.h"

static const gchar *candidate_type_name[] = {"host", "srflx", "prflx", "relay"};

GMainLoop *WIceAgent::s_gloop = nullptr;
GThread *WIceAgent::s_gloopthread = nullptr;

WIceAgent::WIceAgent(IWebsocketServer *wsServer, shared_ptr<WsRequestMsg> wsRequest, gboolean controlling,
                     const gchar *stunAddr, guint stunPort)
{
    _wsServer = wsServer;
    _wsRequest = wsRequest;
    _iceAgent = nice_agent_new(g_main_loop_get_context (s_gloop), NICE_COMPATIBILITY_RFC5245);
    // thread_audio_ = nullptr;
    // this->mOfferAnswerServer = offerAnswerServer;
    if (stunAddr)
    {
        g_object_set(_iceAgent, "stun-server", stunAddr, NULL);
        g_object_set(_iceAgent, "stun-server-port", stunPort, NULL);
    }
    g_object_set(_iceAgent, "upnp", false, NULL);
    g_object_set(_iceAgent, "ice-tcp", false, NULL);
    g_object_set(_iceAgent, "controlling-mode", controlling, NULL);

    std::string strhost ="172.16.208.3";
    NiceAddress addr_local;
    nice_address_init (&addr_local);
    nice_address_set_from_string(&addr_local, strhost.c_str());
    nice_agent_add_local_address (_iceAgent, &addr_local);

    g_signal_connect(_iceAgent, "candidate-gathering-done", G_CALLBACK(cbCandidateGatheringDone), this);
    g_signal_connect(_iceAgent, "component-state-changed", G_CALLBACK(cbComponentStateChanged), this);
    g_signal_connect (_iceAgent, "new-selected-pair-full",G_CALLBACK (cbNewSelectedPairFull), this);
    _dtlsSrtp = new WDtlsSrtp;
}

WIceAgent::~WIceAgent()
{
    if (_videoSendThread)
    {
        _stopVideoSendFlag = true;
        _videoSendThread->join();
        delete _videoSendThread;
    }

    for(auto iter :_streamComponetMap )
    {
        uint32_t ncomponet = iter.second;
        for (int inx = 1; inx <= (int)ncomponet; inx++)
        {
            nice_agent_attach_recv(_iceAgent, iter.first, inx, g_main_loop_get_context(s_gloop), NULL, NULL);
        }
    }
    g_object_unref(_iceAgent);

    delete _dtlsSrtp;
}

void WIceAgent::init()
{
    WDtlsSrtp::init(NULL, NULL); // 两个参数都是NULL，那么就是内部产生自定义证书

    g_networking_init();
    s_gloop = g_main_loop_new(NULL, FALSE);
    s_gloopthread = g_thread_new("loop thread", &loopThread, s_gloop);    //三个参数 分别为标识符 线程运行的函数 线程函数的参数
}

void WIceAgent::release()
{
    std::cout << "ClientNiceAgent release" << std::endl;
    g_main_loop_quit (s_gloop);
    g_thread_join (s_gloopthread);
    g_main_loop_unref(s_gloop);
    WDtlsSrtp::release();
    s_gloop = nullptr;
    s_gloopthread = nullptr;
}

bool WIceAgent::startGather(int32_t streamid)
{
    // Attach to the component to receive the data
    // Without this call, candidates cannot be gathered
    std::map < int32_t, uint32_t>::iterator iter = _streamComponetMap.find(streamid);
    if (_streamComponetMap.end() == iter)
    {
        return false;
    }
    uint32_t ncomponet = iter->second;
    for (int inx = 1; inx <= (int)ncomponet; inx++)
    {
        nice_agent_attach_recv(_iceAgent, streamid, inx, g_main_loop_get_context(s_gloop), cbNiceRecv, this);
    }
    if (!nice_agent_gather_candidates(_iceAgent, streamid))
    {
        printf("Failed to start candidate gathering\n");
        return false;
    }
    return true;
}

int32_t WIceAgent::addStream(const char *szstreamname, uint32_t componentnum)
{
    guint stream_id = nice_agent_add_stream(_iceAgent, componentnum);
    if (stream_id == 0)
    {
        std::cout << "Failed to add stream" << std::endl;
    }
    else
    {
        nice_agent_set_stream_name (_iceAgent, stream_id, szstreamname);
        _streamComponetMap[stream_id] = componentnum;
    }
    // std::cout << "get here stream id is " << stream_id << endl;
    _dtlsSrtp->create(this, 1, 1, DTLS_ROLE_SERVER);
    return stream_id;
}

bool WIceAgent::setIceRecvCb(IIceRecvCb *iceRecvCb)
{
    _iceRecvCb = iceRecvCb;
    return true;
}

bool WIceAgent::setRemoteSdp(const char *sdp)
{
    _remoteSdp = sdp;
    return true;
}

bool WIceAgent::setRemoteCandidate(const char *szcandidate)
{
    std::string szsdp = _remoteSdp +"a="+ szcandidate + "\r\n";
    gchar* ufrag = NULL;
    gchar* pwd = NULL;
    GSList * plist = nice_agent_parse_remote_stream_sdp(_iceAgent, 1, szsdp.c_str(), &ufrag, &pwd);
    if (ufrag && pwd && g_slist_length(plist) > 0)
    {
        ufrag[strlen(ufrag)-1] = 0;
        pwd[strlen(pwd)-1] = 0;

        NiceCandidate* c = (NiceCandidate*)g_slist_nth(plist, 0)->data;

        if (!nice_agent_set_remote_credentials(_iceAgent, 1, ufrag, pwd))
        {
            g_message("failed to set remote credentials");
            return false;
        }

        // Note: this will trigger the start of negotiation.

        if (nice_agent_set_remote_candidates(_iceAgent, 1, 1, plist) < 1)
        {
            g_message("failed to set remote candidates");
            return false;
        }
        g_free(ufrag);
        g_free(pwd);
        //g_slist_free(plist);
        g_slist_free_full(plist, (GDestroyNotify)&nice_candidate_free);
        return true;
    }
    return false;
}

bool WIceAgent::sendData(int32_t streamid, uint32_t componentid, guint len, gchar *buf)
{
    int nret = nice_agent_send(_iceAgent, streamid, componentid, len, buf);
    return nret;
}

bool WIceAgent::sendDataNeedProtect(int32_t streamid, uint32_t componentid, guint len, gchar *buf)
{
    int protectedlen = len;

    int res = srtp_protect(_dtlsSrtp->_srtpOut, buf, &protectedlen);
    //~ JANUS_LOG(LOG_VERB, "[%I64u] ... SRTP protect %s (len=%d-->%d)...\n", handle->handle_id, janus_get_srtp_error(res), pkt->length, protectedlen);
    if(res != srtp_err_status_ok)
    {
        rtp_header *header = (rtp_header *)buf;
        guint32 timestamp = ntohl(header->timestamp);
        guint16 seq = ntohs(header->seq_number);
        printf("SRTP protect error... %d (len=%d-->%d, ts=%d, seq=%d)\n", res, len, protectedlen, timestamp, seq);
        return false;
    }
    else
    {
        /* Shoot! */
        //~ JANUS_LOG(LOG_VERB, "[%I64u] ... Sending SRTP packet (pt=%u, ssrc=%u, seq=%u, ts=%u)...\n", handle->handle_id,
        //~ header->type, ntohl(header->ssrc), ntohs(header->seq_number), ntohl(header->timestamp));
        int sent = nice_agent_send(_iceAgent, streamid, componentid, protectedlen, buf);
        if(sent < protectedlen)
        {
            printf("only sent %d bytes? (was %d)\n", sent, protectedlen);
        }
    }
    return true;
}

void WIceAgent::getRtspRtpVideoTotalLen(const uint8_t *pbuffer, uint32_t dwbuflen, uint32_t &ntotallen, uint32_t &numbernalus)
{
    ntotallen = 0;
    numbernalus = 0;
    uint8_t* pdata = (uint8_t*)pbuffer; // first 5 byte is the flv video prefix
    uint8_t* pend = (uint8_t*)pbuffer+dwbuflen; // the last 4 bytes is the flv last tag len
    while (pdata < pend)
    {
        int nnallen = (pdata[0]<<24) | (pdata[1]<<16) | (pdata[2]<<8) | (pdata[3]);
        pdata += 4;

        if ((nnallen-1) <= MAX_RTP_PKT_LENGTH)
        {
            ntotallen += 4 + 12 + nnallen;        //may be max_len + 1
        }
        else
        {
            int npacket = (nnallen - 1 + MAX_RTP_PKT_LENGTH - 1) / MAX_RTP_PKT_LENGTH;
            ntotallen += npacket * (4 + 2 + 12) + nnallen - 1;
        }
        pdata += nnallen;
        numbernalus += 1;
    }
    // when rtp over rtsp
    if (pdata != pend)
    {
        printf("**************************************************error rtp total len wrong\r\n");
    }
}

void WIceAgent::setBufRtpVideoHeader(uint8_t *pbuffer, uint32_t dwssrc, uint32_t dwtimestample, uint16_t dwseqnum, bool marker)
{
    rtp_header* rtp_hdr = (rtp_header*)pbuffer;
    rtp_hdr->type     = 96;
    rtp_hdr->version     = 2;
    rtp_hdr->ssrc        = htonl(dwssrc);
    rtp_hdr->timestamp=htonl(dwtimestample);
    rtp_hdr->markerbit = marker;
    rtp_hdr->seq_number = htons(dwseqnum);
}

void WIceAgent::candidateGatheringDone(int32_t stream_id)
{
    gchar *local_ufrag = NULL;
    gchar *local_password = NULL;
    gchar ipaddr[INET6_ADDRSTRLEN];
    GSList *cands = NULL;

    if (!nice_agent_get_local_credentials(_iceAgent, stream_id, &local_ufrag, &local_password))
    {
        return;
    }

    cands = nice_agent_get_local_candidates(_iceAgent, stream_id, 1);
    printf("%s %s", local_ufrag, local_password);

    // 这里只使用本地第0个ice Candidate，实际上就是自己在构造函数中传入ip的地址
    NiceCandidate *c = (NiceCandidate *)g_slist_nth(cands, 0)->data;

    nice_address_to_string(&c->addr, ipaddr);

    // (foundation),(prio),(addr),(port),(type)
    printf("%s, %u, %s, %u, %s\r\n", c->foundation, c->priority, ipaddr, nice_address_get_port(&c->addr),
           candidate_type_name[c->type]);

    int nssrc = 12345678;
    char szsdp[1024*10] = {0};
    if (1)
    {
        sprintf(szsdp,
                "v=0\r\n"
                "o=- 1495799811084970 1495799811084970 IN IP4 %s\r\n"
                "s=Streaming Test\r\n"
                "t=0 0\r\n"
                "a=group:BUNDLE video\r\n"
                "a=msid-semantic: WMS janus\r\n"
                "m=video 1 RTP/SAVPF 96\r\n"
                "c=IN IP4 %s\r\n"
                "a=mid:video\r\n"
                "a=sendrecv\r\n"
                "a=msid:wenkeTestStreamId wenkeTestTrackId\r\n"
                "a=rtcp-mux\r\n"
                "a=ice-ufrag:%s\r\n"
                "a=ice-pwd:%s\r\n"
                "a=ice-options:trickle\r\n"
                "a=fingerprint:sha-256 %s\r\n"
                "a=setup:actpass\r\n"
                "a=connection:new\r\n"
                "a=rtpmap:96 H264/90000\r\n"
                "a=ssrc:%d cname:wenkeTest\r\n"
                "a=ssrc:%d msid:wenkeTestStreamId wenkeTestTrackId\r\n"
                "a=ssrc:%d mslabel:wenkeTestStreamId\r\n"
                "a=ssrc:%d label:wenkeTestTrackId\r\n"
                "a=candidate:%s 1 udp %u %s %d typ %s\r\n",
                ipaddr, ipaddr,
                local_ufrag, local_password, WDtlsSrtp::getLocalFingerprint(),
                nssrc, nssrc, nssrc, nssrc,
                c->foundation, c->priority, ipaddr, nice_address_get_port(&c->addr),
                candidate_type_name[c->type]
                );
    }
    else
    {
        sprintf(szsdp,
                "v=0\r\n"
                "o=- 1495799811084970 1495799811084970 IN IP4 %s\r\n"
                "s=Streaming Test\r\n"
                "t=0 0\r\na=group:BUNDLE audio\r\n"
                "a=msid-semantic: WMS janus\r\n"
                "m=audio 1 RTP/SAVPF 0\r\n"
                "c=IN IP4 %s\r\n"
                "a=mid:audio\r\n"
                "a=sendrecv\r\n"
                "a=rtcp-mux\r\n"
                "a=ice-ufrag:%s\r\n"
                "a=ice-pwd:%s\r\n"
                "a=ice-options:trickle\r\n"
                "a=fingerprint:sha-256 %s\r\n"
                "a=setup:actpass\r\n"
                "a=connection:new\r\n"
                "a=rtpmap:0 PCMU/8000\r\n"
                "a=ssrc:%d cname:janusaudio\r\n"
                "a=ssrc:%d msid:janus janusa0\r\n"
                "a=ssrc:%d mslabel:janus\r\n"
                "a=ssrc:%d label:janusa0\r\n"
                "a=candidate:%s 1 udp %u %s %d typ %s\r\n",
                ipaddr, ipaddr,
                local_ufrag, local_password, WDtlsSrtp::getLocalFingerprint(),
                nssrc, nssrc, nssrc, nssrc,
                c->foundation, c->priority, ipaddr, nice_address_get_port(&c->addr),
                candidate_type_name[c->type]
                );
    }
    SignalOrderInfo orderInfo;
    orderInfo.orderType = "createOffer";
    orderInfo.sdpInfo.type = "offer";
    orderInfo.sdpInfo.sdp = szsdp;
    _wsServer->sendMsg(_wsRequest, orderInfo.toJson());
    printf("create sdp is: \r\n%s\r\n", szsdp);

    if (local_ufrag)
    {
        g_free(local_ufrag);
    }

    if (local_password)
    {
        g_free(local_password);
    }

    if (cands)
    {
        g_slist_free_full(cands, (GDestroyNotify)&nice_candidate_free);
    }
}

void WIceAgent::componentStateChanged(int32_t streamid, uint32_t componentid, guint state)
{
    std::cout << this <<" state changed " << streamid << ":"<< componentid << " " << nice_component_state_to_string((NiceComponentState )state)
              << state << std::endl;
}

void WIceAgent::newSelectedPairFull(guint stream_id, guint component_id, NiceCandidate *lcandidate, NiceCandidate *rcandidate)
{
    std::cout << this << "Component is ready enough, starting DTLS handshake...\n";
    _dtlsSrtp->handshake();
}

void WIceAgent::niceRecvData(int32_t streamid, uint32_t componentid, guint len, gchar *buf)
{
    // std::cout << this << " recv data from stream: " << streamid << " componetid: " << componentid << " ,len: " << len << " ,first data :"<< (int)buf[0] <<std::endl;

    int buflen = len;
    if (is_dtls(buf))
    {
        // printf("is dtls\n");
        _dtlsSrtp->incomingMsg(buf, len);
    }
    else if (is_rtp(buf))
    {
        // printf("is rtp \n");
        _dtlsSrtp->srtpUnprotectRtpBuf(buf, &buflen);
        if (_iceRecvCb)
        {
            _iceRecvCb->handleRecvRtp(_wsRequest, buf, buflen);
        }
//        int plen;
//        char* payload = janus_rtp_payload(buf, buflen, &plen);
//        printf("PLEN:%d \n",plen);
    }
    else if (is_rtcp(buf))
    {
        _dtlsSrtp->srtpUnprotectRtcpBuf(buf, &buflen);
//        printf("is rtcp, header data is: \n");
//        for (int i = 0; i < 4; i++)
//        {
//            printf("%02x ", (unsigned char)buf[i]);
//        }
//        printf("\n");
//        if (!_videoSendThread)
//        {
//            printf("ready to start video send thread!\n");
//            _stopVideoSendFlag = false;
//            _videoSendThread = new std::thread(&WIceAgent::readSendVideo, this);

//        }
    }
    else {
        printf("is other \n");
    }

}

void *WIceAgent::loopThread(void *data)
{
    GMainLoop* ploop = (GMainLoop*)data;
    std::cout << "loop thread going..." << std::endl;
    g_main_loop_run(ploop);
    std::cout << "loop thread quit..." << std::endl;
    return nullptr;
}

void WIceAgent::cbCandidateGatheringDone(NiceAgent *agent, guint stream_id, gpointer data)
{
    WIceAgent *pAgent = (WIceAgent*)data;
    pAgent->candidateGatheringDone(stream_id);
}

void WIceAgent::cbComponentStateChanged(NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data)
{
    WIceAgent* pAgent = (WIceAgent*)data;
    pAgent->componentStateChanged(stream_id, component_id, state);
}

void WIceAgent::cbNewSelectedPairFull(NiceAgent *agent, guint stream_id, guint component_id, NiceCandidate *lcandidate, NiceCandidate *rcandidate, gpointer data)
{
    WIceAgent* pAgent = (WIceAgent*)data;
    pAgent->newSelectedPairFull(stream_id, component_id, lcandidate, rcandidate);
}

void WIceAgent::cbNiceRecv(NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer data)
{
    WIceAgent *pAgent = (WIceAgent*)data;
    pAgent->niceRecvData(stream_id, component_id, len, buf);
}

void WIceAgent::readSendVideo()
{
    // ./file/music.mulaw payload is 0
    char rtpbuffer[8*1024] = {0};
    gint16 seq = 1;
    gint32 ts = 0;

    rtp_header *header = (rtp_header *)rtpbuffer;
    header->version = 2;
    header->markerbit = 0;
    header->type = 96;
    header->seq_number = htons(seq);
    header->timestamp = htonl(ts);
    header->ssrc = htonl(12345678);

    string filename = "/home/wenke/Videos/testokmy.h264";
    FILE *videoFile = fopen(filename.c_str(), "rb");
    if (!videoFile)
    {
        printf("can not open video file:%s\n", filename.c_str());
        return;
    }
    printf("open video file success:%s\n", filename.c_str());

    uint32_t dwLen = 0;
    uint8_t* buffer = new uint8_t[1*1024*1024];
    uint32_t dwrealbufLen = 0;
    uint8_t* realbuffer = new uint8_t[1*1024*1024];

    while (!_stopVideoSendFlag)
    {
        this_thread::sleep_for(chrono::seconds(10));
        int nRet = fread(&dwLen, 1, 4, videoFile);
        if (4 != nRet)
        {
            break;
        }

        nRet = fread(buffer, 1, dwLen, videoFile);
        if (nRet != dwLen)
        {
            break;
        }

        if(feof(videoFile))
        {
            printf("file is over\r\n");
            break;
        }

        H264Frame tframe(buffer, dwLen, 0);
        dwrealbufLen = tframe.GetTotalFrameSize();
        tframe.WriteFrameToBuffer(realbuffer);

        uint32_t dwtotallen = 0;
        uint32_t dwnumnalus = 0;
        getRtspRtpVideoTotalLen(realbuffer, dwrealbufLen, dwtotallen, dwnumnalus);

        uint8_t* pdata = (uint8_t*)realbuffer;
        uint8_t* pend = (uint8_t*)realbuffer+dwrealbufLen;

        bool bmarker = false;
        for (int inx = 0; inx < dwnumnalus; inx++)
        {
            if (inx == dwnumnalus-1) { bmarker = true;}

            int nnallen = (pdata[0]<<24) | (pdata[1]<<16) | (pdata[2]<<8) | (pdata[3]);
            pdata += 4;

            uint8_t bynaltype = pdata[0];
            if ((nnallen-1) <= MAX_RTP_PKT_LENGTH)
            {
                memset(rtpbuffer, 0, sizeof(rtpbuffer));
                setBufRtpVideoHeader((uint8_t*)rtpbuffer, 12345678, ts, seq++, bmarker);
                memcpy(&rtpbuffer[12], pdata, nnallen);
                sendDataNeedProtect(1, 1, 12+nnallen, rtpbuffer);
            }
            else
            {
                bool blastfu = false;
                int npacket = (nnallen - 1 + MAX_RTP_PKT_LENGTH - 1) / MAX_RTP_PKT_LENGTH;
                for(int inxpack = 0; inxpack < npacket; inxpack++)
                {
                    int npacketsize = MAX_RTP_PKT_LENGTH;
                    uint8_t* psrcnalu = pdata + 1 + inxpack * MAX_RTP_PKT_LENGTH;
                    if (inxpack == npacket-1)
                    {
                        npacketsize = (nnallen - 1) - inxpack * MAX_RTP_PKT_LENGTH; // 2801 nallen
                        blastfu = true;
                    }
                    memset(rtpbuffer, 0, sizeof(rtpbuffer));

                    setBufRtpVideoHeader((uint8_t*)rtpbuffer, 12345678, ts, seq++, bmarker&blastfu);
                    int dwdestinx = 0;
                    dwdestinx += 12;

                    rtpbuffer[dwdestinx++] = (bynaltype & 0xe0) | 28;

                    if (inxpack == 0)
                    {
                        rtpbuffer[dwdestinx++] = (bynaltype & 0x1f) | 0x80;//S
                    }
                    // last pack
                    else if (inxpack == npacket - 1)
                    {
                        rtpbuffer[dwdestinx++] = (bynaltype & 0x1f) | 0x40;//E
                    }
                    else
                    {
                        rtpbuffer[dwdestinx++] = bynaltype & 0x1f;
                    }
                    memcpy(&rtpbuffer[dwdestinx], psrcnalu, npacketsize);
                    sendDataNeedProtect(1, 1, dwdestinx+npacketsize, rtpbuffer);
                    dwdestinx += npacketsize;
                }
            }

            pdata += nnallen;
        }

        ts += 40*90;
    }

    fclose(videoFile);
    delete [] buffer;
    delete [] realbuffer;
}
